#include <cstdlib>
#include <iostream>
#include "NclSignalManager.h"

enum STATE
{
	SLEEPING,
	OCCURRING,
	PAUSED,
	STATE_LENGTH
};
enum EVENT
{
	PRESENTATION,
	SELECTION,
	ATRIBUTION,
	COMPOSITION,
	POINTOVER, 	// not used for instance
	DRAG, 		// not used for instance
	FOCUS, 		// not used for instance
	EVENT_LENGTH
};
enum ACTION
{
	START,
	STOP,
	PAUSE,
	RESUME,
	ABORT,
	ACTION_LENGTH
};
enum TRANSITION
{
	STARTS,
	STOPS,
	PAUSES,
	RESUMES,
	ABORTS,
	TRANSITION_LENGTH
};

// NCL Language 
// Description: State Machine to manipulate composition elements and medias
// Reference: Pattern State
class NclStateMachine
{
	// currente state
	class State* m_current_state;
	STATE m_state_id;
	STATE m_previus_state_id;
public:
	NclStateMachine();
	void setState(State* s);
	void sleeping();
	void occurring();
	void paused();
	STATE getState();
	STATE getPreviousState();
};

// abstract State class
class State
{
public:
	State(){}
	virtual void  sleeping(NclStateMachine* nclStateWrapper)=0;
	virtual void occurring(NclStateMachine* nclStateWrapper)=0;
	virtual void    paused(NclStateMachine* nclStateWrapper)=0;
};


// implementation of Sleeping state
class Sleeping : public State
{
public:
	Sleeping()
	{
		
		//std::cout << "creating Sleeping" << std::endl;
	}
	~Sleeping()
	{
		//std::cout << "destroying Sleeping" << std::endl;
	}

	void sleeping(NclStateMachine* nclStateWrapper)
	{
		std::cout << "Already Sleeping" << std::endl;
	}
	void occurring(NclStateMachine* nclStateWrapper);
	void    paused(NclStateMachine* nclStateWrapper)
	{
		std::cout << "Forbiden transition from Sleeping to Paused" << std::endl;
	}
};

// implementation of Ocurring state
class Occurring : public State
{
public:
	Occurring()
	{
		//std::cout << "creating Occurring" << std::endl;
	}
	~Occurring()
	{
		//std::cout << "destroying Occurring" << std::endl;
	}

	void occurring(NclStateMachine* nclStateWrapper)
	{
		std::cout << "Already Ocurring" << std::endl;
	}
	void sleeping(NclStateMachine* nclStateWrapper);
	void   paused(NclStateMachine* nclStateWrapper);

};

// implementation of Paused state
class Paused : public State
{
public:
	Paused()
	{
		//std::cout << "creating Paused" << std::endl;
	}
	~Paused()
	{
		//std::cout << "destroying Paused" << std::endl;
	}

	void paused(NclStateMachine* nclStateWrapper)
	{
		std::cout << "Already Paused" << std::endl;
	}
	void  sleeping(NclStateMachine* nclStateWrapper);
	void occurring(NclStateMachine* nclStateWrapper);

};

class NclEvent
{
	EVENT event;
public:
	NclEvent(): event(EVENT_LENGTH){}
	void set(EVENT e)
	{
		event=e;
	}
	EVENT get()
	{
		return event;
	}
};

class NclAction
{
	ACTION action;
public:
	NclAction(): action(ACTION_LENGTH){}
	void set(ACTION a)
	{
		action=a;
	}
	ACTION get()
	{
		return action;
	}
};

class NclTransition
{
	TRANSITION transition;
public:
	NclTransition(): transition(TRANSITION_LENGTH){}
	void set(TRANSITION t)
	{
		transition=t;
	}
	TRANSITION get()
	{
		return transition;
	}
	std::string getTransitionString(TRANSITION t)
	{	
		std::string strTransition[] = {"STARTS","STOPS","PAUSES","RESUMES","ABORTS"};

		return strTransition[(int)t];
	}
};

class NclStateManager
{
	NclTransition	transition;
	NclAction 	action;
	NclEvent 	event;
  	NclStateMachine state;
	int repetitions;
	int occurrences;
	NclSignalManager* m_signal;
	protected:long idElement;

public:
	NclStateManager(const long element): repetitions(0), occurrences(0),idElement(element)
	{
		m_signal = &NclSignalManager::getInstance();
	}
	NclStateManager(const ACTION a, const EVENT e, const long element): repetitions(0), occurrences(0),idElement(element)
	{
		m_signal = &NclSignalManager::getInstance();
		event.set(e);
		execAction(a);
	}
	NclStateManager(const long element,const int repeat): repetitions(repeat), occurrences(0),idElement(element){}


	void start()
	{
		if(state.getState()==SLEEPING)
		{
			action.set(START);
			state.occurring();
			transition.set(STARTS);
			notifyChange();
		}
		else
		{
			std::cout << "Forbiden action from no Sleeping state" << std::endl;
		}

	}
	void stop()
	{
		if(state.getState()!=SLEEPING)
		{
			action.set(STOP);
			state.sleeping();
			transition.set(STOPS);
			occurrences++;
			if(repetitions)
			{
				repetitions--;
				notifyChange();
				start();
			}
			else
			{
				notifyChange();
			}
		}
		else
		{
			std::cout << "Forbiden action from Sleeping state" << std::endl;
		}

	}
	void pause()
	{
		if(state.getState()==OCCURRING)
		{
			action.set(PAUSE);
			state.paused();
			transition.set(PAUSES);
			notifyChange();
		}
		else
		{
			std::cout << "Forbiden action from Sleeping state" << std::endl;
		}
	}
	void resume()
	{
		if(state.getState()==PAUSED)
		{
			action.set(RESUME);
			state.occurring();
			transition.set(RESUMES);
			notifyChange();
		}
		else
		{
			std::cout << "Forbiden action from Non Paused state" << std::endl;
		}
	}
	void abort()
	{
		if(state.getState()!=SLEEPING)
		{
			action.set(ABORT);
			state.sleeping();
			transition.set(ABORTS);
			if(repetitions)
			{
				repetitions=0;
			}
			notifyChange();
		}
		else
		{
			std::cout << "Forbiden action from Sleeping state" << std::endl;
		}
	}

	void execAction(ACTION a)
	{
		// define a function pointer for each Action of State Machine
		void(NclStateManager:: *pAction[])() = 
  		{
    			&NclStateManager::start, 
	    		&NclStateManager::stop, 
    			&NclStateManager::pause, 
    			&NclStateManager::resume, 
	    		&NclStateManager::abort, 
		};
		// call function pointers		
	    	(this->*pAction[a])();
	}

	STATE getState()
	{
		return state.getState();
	}

	TRANSITION getTransition()
	{
		return transition.get();
	}

	int getRepetitions()
	{
		return repetitions;
	}

	int getOccurrences()
	{
		return occurrences;
	}

	std::string getStateString(STATE s)
	{	
		std::string strState[] = {"SLEEPING","OCCURRING","PAUSED"};
		return strState[(int)s];
	}

	std::string getActionString(ACTION a)
	{	
		std::string strAction[] = {"START","STOP","PAUSE","RESUME","ABORT","ACTION_LENGTH"};
		return strAction[(int)a];
	}

	std::string getEventString(EVENT e)
	{	
		std::string strEvent[] = {"PRESENTATION","SELECTION","ATRIBUTION","COMPOSITION","POINTOVER","DRAG","FOCUS","EVENT_LENGTH"};
		return strEvent[(int)e];
	}

	// signal support
	// send Signal
	void notifyChange();
};

